package ch.ethz.fcl.metrobuzz.viewer;

import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;

public class Camera {
	private float panX = 0;
	private float panZ = 0;
	private float panY = 0;
	private float initDistance = 2.5f;

	private float distance = initDistance;

	private float maxDistance = 1.2f;

	private float trackX = 0f;
	private float trackY = 0f;

	private MBViewer.ProjectionType proj;

	private boolean panEnabled = true;

	private float whRatio; // width height ratio of the screen

	public Camera(MBViewer.ProjectionType proj) {
		this.proj = proj;
	}

	public void setup(int width, int height) {
		if (proj == MBViewer.ProjectionType.Perspective)
			setupPersView(width, height);
		else if (proj == MBViewer.ProjectionType.Ortho)
			setupOthoView(width, height);

		GL11.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
	}

	public void update() {
		if (proj == MBViewer.ProjectionType.Perspective) {
			GLU.gluLookAt(0.0f, 0.0f, distance, 0.0f, -0.0f, 0.0f, 0, 1, 0);
			if (panEnabled) {
				GL11.glRotatef(panX, 1, 0, 0);
				GL11.glRotatef(panY, 0, 1, 0);
				GL11.glRotatef(panZ, 0, 0, 1);
			}
		}

		else if (proj == MBViewer.ProjectionType.Ortho) {
			GL11.glMatrixMode(GL11.GL_PROJECTION);
			GL11.glLoadIdentity();

			float minX = -0.5f, maxX = 0.5f;
			float minY = minX / whRatio * 1.08f, maxY = maxX / whRatio * 1.08f;
			GL11.glOrtho(minX * distance, maxX * distance, minY * distance,
					maxY * distance, -10, 10);
			GL11.glMatrixMode(GL11.GL_MODELVIEW);
			GL11.glLoadIdentity();
		}
		GL11.glTranslatef(trackX, trackY, 0.0f);
	}

	public void setupPersView(int width, int height) {
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glPushMatrix();
		GL11.glLoadIdentity();
		GLU.gluPerspective(35, (float) width / (float) height, 0.01f, 10);

		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glPushMatrix();
		GL11.glLoadIdentity();
	}

	public void setupOthoView(int width, int height) {
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glPushMatrix();
		GL11.glLoadIdentity();

		whRatio = (float) width / height;
		float minX = -0.5f, maxX = 0.5f;
		float minY = minX / whRatio / 1.1f, maxY = maxX / whRatio / 1.1f;
		GL11.glOrtho(minX, maxX, minY, maxY, -10, 10);
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glPushMatrix();
		GL11.glLoadIdentity();
	}

	public float getPanX() {
		return panX;
	}

	public void setPanX(float angle) {
		this.panX = angle;
	}

	public void addPanX(float delta) {
		this.panX += delta;
	}

	public float getPanY() {
		return panY;
	}

	public void setPanY(float angle) {
		this.panY = angle;
	}

	public void addPanY(float delta) {
		this.panY += delta;
	}

	public float getPanZ() {
		return panZ;
	}

	public void setPanZ(float angle) {
		this.panZ = angle;
	}

	public void addPanZ(float delta) {
		this.panZ += delta;
	}

	public float getDistance() {
		return distance;
	}

	public void initDistance(float distance) {
		this.initDistance = Math.max(maxDistance, distance);
		this.distance = initDistance;
	}

	public void setDistance(float distance) {
		this.distance = Math.max(maxDistance, distance);
	}

	public void addDistance(float delta) {
		delta /= 10;
		distance = Math.max(0.01f, distance + delta);
		distance = Math.min(maxDistance, distance + delta);
	}

	public float getTrackX() {
		return trackX;
	}

	public void setTrackX(float track) {
		this.trackX = track;
	}

	public void addTrackX(float delta) {
		this.trackX += delta;
	}

	public float getTrackY() {
		return trackY;
	}

	public void setTrackY(float track) {
		this.trackY = track;
	}

	public void addTrackY(float delta) {
		this.trackY += delta;
	}

	public void enablePan() {
		this.panEnabled = true;
	}

	public void disablePan() {
		this.panEnabled = false;
	}

	public float getInitDistance() {
		return initDistance;
	}
}
